package com.pan.utils;

import com.pan.action.chain.ChainAction;
import com.pan.action.MyAction;
import com.pan.anchor.MyAnchor;
import com.pan.listener.MyListener;
import com.pan.show.factory.RecordFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.awt.*;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public enum ApplicationConfig {
    INSTANCE;

    private static final String CONFIG_NAME="ApplicationConfig.xml";


    private Map<String,Object> configPool ;
    private Document document;
    private Element rootElement;
    {
        configPool = new HashMap();
        InputStream resourceAsStream = ApplicationConfig.class.getClassLoader().getResourceAsStream(CONFIG_NAME);
        DocumentBuilder documentBuilder = null;
        try {
            documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            document = documentBuilder.parse(resourceAsStream);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        document.normalizeDocument();
        rootElement = document.getDocumentElement();
    }


    private List<Element> getChildrenByName(Element element, String name){
        NodeList childNodes = element.getChildNodes();
        List<Element> result = new ArrayList<>();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(name)){
                result.add((Element) node);
            }
        }
        return result;
    }

    private Element getFirstChildByName(Element element,String name){
        NodeList childNodes = element.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(name)){
                return (Element) node;
            }
        }
        return null;
    }


    private Element getNestElement(Element element,String... nodeNames){
        if (nodeNames.length == 0){
            return null;
        }
        Element currentElement = element;
        for (String nodeName : nodeNames) {
            Element firstChildByName = getFirstChildByName(currentElement, nodeName);
            if (firstChildByName == null) return null;
            else currentElement = firstChildByName;
        }
        return currentElement;
    }

    private boolean hasChildByName(Element element,String name){
        Element child = getFirstChildByName(element, name);
        if (child == null) return false;
        return true;
    }

    public Font getFont(){
        if (configPool.containsKey("font")){
            return (Font) configPool.get("font");
        }
        Element fontElement = getFirstChildByName(rootElement, "font");
        String fontName = getFirstChildByName(fontElement,"name").getTextContent().trim();
        int fontStyle = Integer.parseInt(getFirstChildByName(fontElement,"style").getTextContent().trim());
        int fontSize = Integer.parseInt(getFirstChildByName(fontElement,"size").getTextContent().trim());
        Font font = new Font(fontName,fontStyle,fontSize);
        configPool.put("font",font);
        return font;
    }

    public int[] getOriginXY(){
        if (configPool.containsKey("originXY")){
            return (int[]) configPool.get("originXY");
        }
        Element positionElement = getNestElement(rootElement, "origin", "position");
        String xStr = getFirstChildByName(positionElement, "x").getTextContent().trim();
        String yStr = getFirstChildByName(positionElement, "x").getTextContent().trim();
        int x = Integer.parseInt(xStr);
        int y = Integer.parseInt(yStr);
        int[] result = new int[]{x,y};
        configPool.put("originXY",result);
        return result;
    }

    private Object newInstanceByClassName(String className){
        Class clazz = null;
        Object obj = null;
        try {
            clazz = Class.forName(className);
            obj = clazz.getConstructor().newInstance();
//            obj = clazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e+" className: "+className);
        }
        return obj;
    }
    public String getAnchorClassName(){
        return getNestElement(rootElement,"anchor","class").getTextContent().trim();
    }

    public MyAnchor getAnchor(){
        String className = getAnchorClassName();
        MyAnchor anchor = (MyAnchor) newInstanceByClassName(className);
        return anchor;
    }


    public String getListenerClassName(){
        return getNestElement(rootElement,"listener","class").getTextContent().trim();
    }

    public MyListener getListener(){
        String className = getListenerClassName();
        MyListener listener  = (MyListener) newInstanceByClassName(className);
        return listener;
    }

    public List<MyAction> getActionList(){
        Element actionsElement = getFirstChildByName(rootElement, "actions");
        List<Element> actionElementList = getChildrenByName(actionsElement, "action");
        List<MyAction> resultList = new ArrayList<>();
        for (Element actionElement : actionElementList){
            if (hasChildByName(actionElement,"chain")){
                Element chainElement = getFirstChildByName(actionElement,"chain");
                List<Element> classElements = getChildrenByName(chainElement,"class");
                List<ChainAction> chainActionList = classElements.stream().map(Element::getTextContent)
                        .map(ApplicationConfig.INSTANCE::newInstanceByClassName)
                        .map(a -> (ChainAction) a).collect(Collectors.toList());
                chainActionList.stream().reduce((a,b)->a.setNext(b));
                resultList.add(chainActionList.get(0));
            }else {
                String actionClassName = getFirstChildByName(actionElement, "class").getTextContent().trim();
                MyAction myAction = (MyAction) newInstanceByClassName(actionClassName);
                resultList.add(myAction);
            }
        }
        return resultList;
    }

    public RecordFactory getRecordFactory(){
        if (configPool.containsKey("recordFactory")){
            return (RecordFactory) configPool.get("recordFactory");
        }
        String type = getNestElement(rootElement, "show","record","type").getTextContent().trim();
        Element typeElement = getNestElement(rootElement, "show", "factory", type);
        if (typeElement == null){
            throw new RuntimeException("No Such RecordFactory: "+type);
        }
        String factoryClassName = typeElement.getTextContent().trim();
        RecordFactory recordFactory = (RecordFactory) newInstanceByClassName(factoryClassName);
        configPool.put("recordFactory",recordFactory);
        return recordFactory;
    }

    public String getRecordClassName(){
        Element showElement = getNestElement(rootElement, "show","record");
        String className = getFirstChildByName(showElement, "class").getTextContent().trim();
        return className;
    }

    public boolean getZoomEnable(){
        Element zoomElement = getNestElement(rootElement,"zoom","enable");
        if (zoomElement == null) return false;
        String zoomStr = zoomElement.getTextContent().trim();
        return Boolean.parseBoolean(zoomStr);
    }
    public String getZoomValue(){
        if (!getZoomEnable())return null;
        Element zoomElement = getNestElement(rootElement,"zoom","value");
        if (zoomElement == null)return null;
        return zoomElement.getTextContent().trim();
    }
    public static void main(String[] args) {

    }



}
